/*
 * pusen_shoot.cpp
 *
 *  Created on: May 22, 2019
 *      Author: wang
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <iostream>
#include <string.h>
#include <pthread.h>

#include <linux/videodev2.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <unistd.h>
#include <linux/videodev2.h>

#include <fcntl.h>

#include "yuv2rgb.h"
#include "bmp.h"
#include "pusen_shoot.h"

using namespace std;

#define CAPTURE_DEVICE "/dev/video0"

typedef struct capture_buffer_t
{
        void * start;
        size_t length;
}capture_buffer,*p_capture_buffer;

pusen_shooter::~pusen_shooter()
{
  if(m_video_fd !=0){
      close(m_video_fd);
  }

  if(m_bmp_fd !=0){
      close(m_bmp_fd);
  }
  printf("close all fd!\n");
}

pusen_shooter::pusen_shooter()
{
  //1.initialize member variaty
  m_video_fd=0;
  m_bmp_fd=0;
  m_cb=NULL;
  m_width=240;
  m_height=240;
  m_byte_per_pixel=2;

  //2.open capture device
  m_video_fd=open(CAPTURE_DEVICE,O_RDWR);
  if(m_video_fd == 0){
    printf("open %s fail!\n",CAPTURE_DEVICE);
    return;
  }
  //3.init device
  struct v4l2_streamparm param;
  param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  param.parm.capture.timeperframe.numerator = 10;
  param.parm.capture.timeperframe.denominator = 10;
  param.parm.capture.capturemode = 2;
  if(ioctl(m_video_fd,VIDIOC_S_PARM,&param) < 0)
  {
    printf("VIDIOC_S_PARM %d fail!\n",param.parm.capture.capturemode);
    return;
  }

  //4.select csi0
  int input=0;
  if (ioctl(m_video_fd, VIDIOC_S_INPUT, &input) < 0)
  {
    return;
  }
}

int pusen_shooter::set_resolution(int width,int height,int byte_per_pixel)
{
  //1.set member var
  m_width  = width;
  m_height = height;
  m_byte_per_pixel = byte_per_pixel;

  //2.set format
  struct v4l2_crop crop;
  crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  crop.c.top = 0;
  crop.c.left = 0;
  crop.c.width = width;
  crop.c.height = height;
  if (ioctl(m_video_fd, VIDIOC_S_CROP, &crop) < 0)
  {
      printf("Error: VIDIOC_S_CROP failed\n");
      return 1;
  }

  struct v4l2_format fmt;
  fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  fmt.fmt.pix.width=width;
  fmt.fmt.pix.height = height;
  //fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_JPEG;
  //fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
  fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUV422P;
  fmt.fmt.pix.bytesperline = width;
  fmt.fmt.pix.sizeimage=0;
  fmt.fmt.pix.priv=0;
  //ret=ioctl(fd,VIDIOC_S_FMT,&fmt);
  if(ioctl(m_video_fd, VIDIOC_S_FMT, &fmt) < 0)
  {
      printf("Error: VIDIOC_S_FMT failed\n");
      return 2;
  }

  return 0;
}

int pusen_shooter::register_cb(shooter_callback cb)
{
  m_cb=cb;
  return 0;
}

int pusen_shooter::shoot(char *filename)
{
  //1.malloc buffer
  struct v4l2_requestbuffers req;
  struct v4l2_buffer buf;
  req.count=4;
  req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  req.memory=V4L2_MEMORY_MMAP;
  int ret=ioctl(m_video_fd,VIDIOC_REQBUFS,&req);
  printf("%s %d VIDIOC_REQBUFS ret=%d\n",__FUNCTION__,__LINE__,ret);

  int i=0;
  capture_buffer buffer[4];
  for(i=0;i<req.count;i++){
    buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory=V4L2_MEMORY_MMAP;
    buf.index=i;
    ret=ioctl(m_video_fd,VIDIOC_QUERYBUF,&buf);
    printf("%s %d VIDIOC_QUERYBUF ret=%d\n",__FUNCTION__,__LINE__,ret);

    buffer[i].length=buf.length;
    buffer[i].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,m_video_fd,buf.m.offset);
    printf("QBUF i:%d buf.index:%d length[%d] offset[0x%08x] start[0x%p]\n",i,buf.index,buf.length,buf.m.offset,buffer[i].start);
  }

  //bufer en queue
  for(i=0;i<req.count;i++){
    buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory=V4L2_MEMORY_MMAP;
    buf.index=i;
    ret = ioctl(m_video_fd,VIDIOC_QBUF,&buf);
    printf("%s %d VIDIOC_QBUF ret=%d\n",__FUNCTION__,__LINE__,ret);
  }

  //2.start capture
  enum v4l2_buf_type t;
  t=V4L2_BUF_TYPE_VIDEO_CAPTURE;
  ret=ioctl(m_video_fd,VIDIOC_STREAMON,&t);
  printf("%s %d VIDIOC_STREAMON ret=%d\n",__FUNCTION__,__LINE__,ret);

  //3.wait data frame
  fd_set fds;
  struct timeval tv;
  tv.tv_sec = 0;
  tv.tv_usec = 100000;

  FD_ZERO(&fds);
  FD_SET(m_video_fd,&fds);
  while(1){
      ret=select(m_video_fd+1,&fds,NULL,NULL,&tv);
      if(ret >0){
          printf("%s %d select ret=%d\n",__FUNCTION__,__LINE__,ret);
          break;
      }
  }

  //4.get data
  ret=ioctl(m_video_fd,VIDIOC_DQBUF,&buf);

  //5.convert data
  unsigned char *rgb_buf=new unsigned char[m_width *m_height *3];
  //yuvtorgb0((unsigned char *)buffer[buf.index].start,rgb_buf,m_width,m_height);
  yuvptorgb0((unsigned char *)buffer[buf.index].start,rgb_buf,m_width,m_height);
  ClImage rgb_img;
  rgb_img.width =m_width;
  rgb_img.height=m_height;
  rgb_img.channels =3;
  rgb_img.imageData=rgb_buf;
  clSaveImage(filename,&rgb_img);

  delete[] rgb_buf;
  //4.capture complete
  if(m_cb != NULL){
      m_cb(0,filename);
  }
  return 0;
}
